import 'package:aichat_flutter_wallet/kai_print.dart';
import 'package:aichat_flutter_wallet/module_common/mpc_help/mpcManager.dart';
import 'package:aichat_flutter_wallet/module_common/network/chains_type/ethchain.dart';
import 'package:aichat_flutter_wallet/module_common/network/rpc.dart';
import 'package:encrypt/encrypt.dart';
import 'package:lib_database/lib_database.dart';

import 'chainListTool.dart';

class createWalletTool {
  // 重写 overwrite 钱包
  Future<List<WalletInfo>> overwriteWallet(String key, List<Chain> chains, String uId,
      {String? walletName, bool isMPC = false, String? salt = '', bool? backup = false, bool? isServerBackUp}) async {
    key = isMPC == true ? key : key.toLowerCase();
    if (key.startsWith('0x') && isMPC != true) {
      key = key.substring(2);
    }

    //AES加密
    var encryptedKey = isMPC == true ? key : _encryptKey(key);
    Wallet? wallet = ObjectBox().walletBox.query(Wallet_.uId.equals(uId)).build().findFirst();
    if(wallet==null){
      throw Exception('The old wallet id does not exist and the upgrade failed.');
    }
    bool isMnemonic = key.contains(' ');
    //  修改 分片；
    wallet.key= encryptedKey;
    ObjectBox().walletBox.put(wallet);
    int walletId =  wallet.id;
    var time = DateTime.now().millisecondsSinceEpoch;
    List<WalletInfo> infoList = [];
    List<WalletInfo> newInfoList = [];
    String evmKey = '';
    String evmAddress = '';
    for (var c in chains) {
      String pk;
      String address;
      String chainId = c.evmInfo!.chainId.toString();
      if (c.type == ChainType.eth) {
        if (evmKey.isEmpty || evmAddress.isEmpty) {
          evmKey = isMPC == true ? key : (isMnemonic ? await WalletUtils().generatePkByMnemonic(key, c) : key);

          evmAddress = isMPC == true
              ? WalletUtils().generateMPCAddress(await mpcTool().getAddress(evmKey.split(',')[0],chainId), c)
              : WalletUtils().generateAddress(evmKey, c);
        }
        pk = evmKey;
        address = evmAddress;
      } else {
        pk = isMPC == true ? key : (isMnemonic ? await WalletUtils().generatePkByMnemonic(key, c) : key);
        address = isMPC == true
            ? WalletUtils().generateMPCAddress(await mpcTool().getAddress(pk.split(',')[0],chainId), c)
            : WalletUtils().generateAddress(evmKey, c);
      }
      if (pk.isNotEmpty && address.isNotEmpty) {
        WalletInfo? info = ObjectBox().walletInfoModel.getWalletInfoByWalletIdAndChain(wallet.id, c.shortName);
        if(info==null){
          // 新增
          WalletInfo walletInfo = WalletInfo(
              walletId: walletId,
              createTime: time,
              privateKey: isMPC == true ? pk : _encryptKey(pk),
              chainShortName: c.shortName,
              address: address);
          newInfoList.add(walletInfo);
        }else{
          // 重写
          info.privateKey = isMPC == true ? pk : _encryptKey(pk);
          info.createTime = time;
          infoList.add(info);
        }
      }
      // 添加 对应 chains
    }
    if (infoList.isNotEmpty) {
      // 纯 更新 覆盖
      ObjectBox().walletInfoBox.putMany(infoList);
    }
    if (newInfoList.isNotEmpty) {
      var list = ObjectBox().walletInfoBox.putMany(newInfoList);
      //
      for (var i = 0; i < list.length; i++) {
        createMainTokenToDB(list[i], infoList[i].getChain()!.symbol, infoList[i].getChain()!.shortName);
      }
    }

    return [...infoList,...newInfoList];
  }
  // 创建观察 钱包
  Future<List<WalletInfo>> createWatchWallet(
    String watchAddress,
    List<Chain> chains,
    String uId, {
    String? walletName,
    bool isMPC = false,
    String salt = '',
        int? version=null
  }) async {
    try {
      String key = isMPC ? watchAddress : watchAddress.toLowerCase();
      if (isMPC == false && key.startsWith('0x')) {
        key = key.substring(2);
      }
      //AES加密
      var encryptedKey = isMPC ? key : _encryptKey(key);
      Wallet? wallet =
          ObjectBox().walletBox.query(Wallet_.key.equals(encryptedKey) & Wallet_.uId.equals(uId)).build().findFirst();
      if (null != wallet) {
        for (Chain c in chains) {
          WalletInfo? info = ObjectBox().walletInfoModel.getWalletInfoByWalletIdAndChain(wallet.id, c.shortName);
          if (null != info) {
            // kaiPrint('info  $info');
            return [info];
          }
        }
      }
      int walletId = null != wallet
          ? wallet.id
          : ObjectBox().walletBox.put(Wallet(uId: uId, key: encryptedKey, type: 2, name: walletName)); //2为观察钱包
      var time = DateTime.now().millisecondsSinceEpoch;
      List<WalletInfo> infoList = [];

      // String pk = isMPC == true ? watchAddress : '';
      // String evmAddress = '';
      String evmKey = '';
      String evmAddress = '';
      // String address = watchAddress.toLowerCase();
      for (var c in chains) {
        String pk;
        String address;
        String chainId = c.evmInfo!.chainId.toString();
        if (c.type == ChainType.eth) {
          if (evmKey.isEmpty || evmAddress.isEmpty) {
            evmKey = watchAddress;
            evmAddress = isMPC == true
                ? WalletUtils().generateMPCAddress(await mpcTool().getAddress(evmKey,chainId), c)
                : WalletUtils().generateAddress(evmKey, c);
          }
          pk = evmKey;
          address = evmAddress;
        } else {
          //  非 以太 系列 的 版本 为 1 的 分片 跳过生成 tron 和 solana 的钱包
          if(version==1){
            continue;
          }
          pk = watchAddress;
          address = isMPC == true
              ? WalletUtils().generateMPCAddress(await mpcTool().getAddress(pk.split(',')[0],chainId), c)
              : WalletUtils().generateAddress(evmKey, c);
        }
        WalletInfo walletInfo = WalletInfo(
            walletId: walletId, createTime: time, privateKey: pk, chainShortName: c.shortName, address: address);
        infoList.add(walletInfo);
      }

      kaiPrint('create wallet：${infoList.length}');
      if (infoList.isNotEmpty) {
        List<int> list = ObjectBox().walletInfoBox.putMany(infoList);
        for (var i = 0; i < list.length; i++) {
          createMainTokenToDB(list[i], infoList[i].getChain()!.symbol, infoList[i].getChain()!.shortName);
        }
      }
      return infoList;
    } catch (e) {
      kaiPrint('createWalletTool watch $e');
      return [];
    }
  }

  // 创建所有主币的 币种
  Future<Token?> createMainTokenToDB(int walletInfoId, String symbol, String shortName) async {
    try {
      Token token = Token(
          walletInfoId: walletInfoId,
          contractType: TokenType.native.name,
          contract: '',
          name: shortName,
          balance: '0',
          priceusd: '0',
          change24h: '0',
          symbol: symbol,
          decimals: ChainListTool.callBackDecimals(symbol),
          enable: true,
          isAdded: true);
      kaiPrint('add the ${shortName} main coin：${token.toString()}');
      ObjectBox().tokenModel.addToken(token);
      return null;
    } catch (e) {
      kaiPrint('create main coin failed: $e');
      return null;
    }
  }

  // 创建钱包
  Future<List<WalletInfo>> createWallet(String key, List<Chain> chains, String uId,
      {String? walletName, bool isMPC = false, String? salt = '', bool? backup = false, bool? isServerBackUp}) async {
    key = isMPC == true ? key : key.toLowerCase();
    if (key.startsWith('0x') && isMPC != true) {
      key = key.substring(2);
    }

    //AES加密
    var encryptedKey = isMPC == true ? key : _encryptKey(key);
    Wallet? wallet =
        ObjectBox().walletBox.query(Wallet_.key.equals(encryptedKey) & Wallet_.uId.equals(uId)).build().findFirst();
    if (null != wallet) {
      for (Chain c in chains) {
        WalletInfo? info = ObjectBox().walletInfoModel.getWalletInfoByWalletIdAndChain(wallet.id, c.shortName);
        if (null != info) {
          kaiPrint('createWallet  $info');
          return [info];
        }
      }
    }

    bool isMnemonic = key.contains(' ');

    int walletId = null != wallet
        ? wallet.id
        : ObjectBox().walletBox.put(Wallet(
            salt: salt,
            uId: uId,
            key: encryptedKey,
            type: isMPC == true ? 3 : (isMnemonic ? 0 : 1),
            name: walletName,
            isServerBackUp: isServerBackUp ?? false,
            backup: backup));

    var time = DateTime.now().millisecondsSinceEpoch;
    List<WalletInfo> infoList = [];

    String evmKey = '';
    String evmAddress = '';

    for (var c in chains) {
      String pk;
      String address;
      String chainId = c.evmInfo!.chainId.toString();
      if (c.type == ChainType.eth) {
        if (evmKey.isEmpty || evmAddress.isEmpty) {
          evmKey = isMPC == true ? key : (isMnemonic ? await WalletUtils().generatePkByMnemonic(key, c) : key);

          evmAddress = isMPC == true
              ? WalletUtils().generateMPCAddress(await mpcTool().getAddress(evmKey.split(',')[0],chainId), c)
              : WalletUtils().generateAddress(evmKey, c);
        }
        pk = evmKey;
        address = evmAddress;
      } else {
        pk = isMPC == true ? key : (isMnemonic ? await WalletUtils().generatePkByMnemonic(key, c) : key);
        address = isMPC == true
            ? WalletUtils().generateMPCAddress(await mpcTool().getAddress(pk.split(',')[0],chainId), c)
            : WalletUtils().generateAddress(evmKey, c);
      }
      if (pk.isNotEmpty && address.isNotEmpty) {
        WalletInfo walletInfo = WalletInfo(
            walletId: walletId,
            createTime: time,
            privateKey: pk,
            chainShortName: c.shortName,
            address: address);
        infoList.add(walletInfo);
        // createMainTokenByWalletId(walletInfo);
        // print(walletInfo);
      }
      // 添加 对应 chains
    }
    if (infoList.isNotEmpty) {
      var list = ObjectBox().walletInfoBox.putMany(infoList);
      for (var i = 0; i < list.length; i++) {
        createMainTokenToDB(list[i], infoList[i].getChain()!.symbol, infoList[i].getChain()!.shortName);
      }
    }

    return infoList;
  }

  static final Key _aesKey = Key.fromUtf8('wKJXezVZyYZJaWSP3P0krPGb2ymD6KQg');
  static final IV _iv = IV.fromUtf8('zJBrKbEehdfXvLCp');
  static final _encrypt = Encrypter(AES(_aesKey));

  String _encryptKey(String key) {
    var encrypted = _encrypt.encrypt(key, iv: _iv);
    String encryptedStr = encrypted.base64;
    // var decrypted =
    //     _encrypt.decrypt(Encrypted.fromBase64(encryptedStr), iv: _iv);
    return encryptedStr;
  }

  // 创建钱包链对应的信息
  Future<WalletInfo> createWalletInfo(Wallet wallet, Chain chain) async {
    var time = DateTime.now().millisecondsSinceEpoch;
    var pk = wallet.key;
    var address = wallet.type == 3
        ? WalletUtils().generateMPCAddress(await mpcTool().mpcFragmentGetPublicKey(pk.split(',')[0]), chain)
        : WalletUtils().generateAddress(pk, chain);
    // 帮它 生成 一条 创建新链 的 钱包地址
    WalletInfo walletInfo = WalletInfo(
        walletId: wallet.id, createTime: time, privateKey: pk, chainShortName: chain.shortName, address: address);
    // createMainTokenByWalletId(walletInfo);
    ObjectBox().walletInfoBox.put(walletInfo, mode: PutMode.update);

    return walletInfo;
  }

  //  创建 钱包对应 链 的币种
  static Future<Null> createMainTokenByWalletId(WalletInfo walletInfo) async {
    Chain c = walletInfo.getChain()!;
    String? balance = null;
    String? priceU = null;
    Token? _nativeToken = ObjectBox().tokenModel.getMainTokenByWalletInfoId(walletInfo.id);
    if (_nativeToken == null) {
      //  插入数据
      _nativeToken = Token(
          walletInfoId: walletInfo.id,
          contractType: TokenType.native.name,
          //  待会生成 余额
          balance: balance,
          // 查询U 的价值
          priceusd: priceU,
          contract: '',
          name: '',
          symbol: walletInfo.getChain()!.symbol,
          decimals: 18,
          enable: true,
          isAdded: true);
      ObjectBox().tokenModel.addToken(_nativeToken);
    }

    try {
      String rpc = ObjectBox().nodeInfoModel.getSelectedNodeInfo(c.shortName).url;
      // 获取 主币余额
      RpcClient().ethGetBalance(wallet: walletInfo).then((value) {
        if (value.isNotEmpty) {
          _nativeToken!.balance = value.toString();
          ObjectBox().tokenModel.updateAsyncToken(_nativeToken);
        }
      });
      // 查询 usdt 余额
      EthChain().getErcMainTokenPriceU(c.evmInfo!.chainId.toString(), rpc).then((value) {
        if (value.isNotEmpty) {
          _nativeToken!.priceusd = value.toString();
          ObjectBox().tokenModel.updateAsyncToken(_nativeToken);
        }
      });
    } catch (e) {
      kaiPrint('$e');
    }
  }
}
